iT邦幫忙

2026 iThome 鐵人賽

DAY 1
2

嗨嗨~大家我是愛搞怪的 YC
一個從小被老師嫌文筆差的人,要來挑戰寫 30 天的技術文章,這件事本身就已經很問號了。

總之這個系列要撬開 Spark、DataFusion 與 Comet 的大門
身為 DataFusion Comet 開源專案的貢獻者,把底層的設計邏輯搞清楚本來就是應該的,與其說是寫給大家看,不如說是我逼自己把「為什麼會長成這樣」一層一層講明白,順便讓大家抓我的錯。

這系列不會有:安裝教學、config 大全、貼一堆程式碼叫你自己看,理由很簡單,API 半年換一輪,寫下來很快就錯了,看了也只是打發時間而已。

那就開始今天的主題:1+1+1 > 3 ~ Spark 與 DataFusion、Comet 效能煉金術 ~

C'est parti !
https://ithelp.ithome.com.tw/upload/images/20260817/20183544xThDeyOTpL.jpg


SQL 來打開故事情節

用個 TPC-H Q1 的簡化版來當栗子🌰:

SELECT l_returnflag, SUM(l_extendedprice * (1 - l_discount)) AS revenue
FROM lineitem
WHERE l_shipdate <= DATE '1998-09-01'
GROUP BY l_returnflag

挑這個當例子是因為它剛好寫了四件分別住在不同層的語句:

  • WHERE l_shipdate <= DATE '1998-09-01' — Parquet 檔案裡每一塊資料都附帶最小值和最大值。如果某塊的最小 shipdate 已經大於 1998-09-01,那整塊連解碼都不用,直接跳過,這是儲存格式那一層在做的事。
  • l_extendedprice * (1 - l_discount) — 這個乘法減法每一列都得算一次,沒有捷徑可走,所以它是「能不能一道指令同時算八列」這個問題的舞台,這是硬體與向量化那層。
  • SUM(...) — 累加中的結果必須一直留在記憶體,資料量夠大就會放不下,這是查詢引擎那一層的記憶體管理與 spill。
  • GROUP BY l_returnflag — 同一個 returnflag 的資料散在幾百台機器上,要加總就得先集中到同一台,這就是 shuffle,這是跨界那層,也是 Comet 最主要的戰場。

TPC-H 是業界標準測試集,資料產生器和查詢都公開,想自己跑一遍驗證都是可以的。

問題是:這句話從送進 Spark 到吐出結果,中間經過哪些地方?

一個問題,六個箱子

標準答案大概長這樣:parse → analyze → optimize → physical plan → 執行 → 收集結果
六個箱子,教科書都這樣畫,也沒有錯。
問題出在最後兩個箱子,前面四格是三十年查詢處理研究的直接繼承者,論文很多,寫得很清楚。
「執行」那一格則被畫成一個箱子,裡面塞著一整個系統,而這個系列的三十天故事都來自於那一格裡。

把它撬開,會看到一堆沒有標準答案的選擇:

  • l_shipdate <= '1998-09-01' 這個條件,是誰決定 Parquet 檔案裡哪些 page 要被解碼、哪些可以整塊跳過?
  • 解出來的資料以什麼佈局躺在記憶體?一次解幾列?為什麼是那個數字?
  • 這個比較是一列比一次,還是一道指令比八個 date?
  • SUM 的 hash table 塞不下記憶體的時候,誰讓位、讓多少?
  • 算完的欄式結果,要怎麼交回給一個以列為單位運作的 Spark?

五個問號,五個設計決定,而且每一個都有人做過相反的選擇

所以標題那句「你的查詢是這樣也不是這樣」是這個意思:你看到的 plan 是真的,但它只是一層皮。同一句 SQL 底下可以跑出完全不同的東西,而且效能差好幾倍。

這 30 天怎麼排

順序有五層,由下往上:硬體先講,Comet 排最後。

先說為什麼不反過來
直覺的寫法當然是從 Comet 開始,它是什麼、怎麼裝、有哪些 config,但那樣寫出來會是一份 API 導覽,而且如前面說的,很快就過期 0 人在意的文章。

由下往上的好處是,每個設計都會變成結論而不是規定。先問「現代 CPU 喜歡什麼形狀的資料」,Arrow 的 buffer 佈局就不再是規格,而是一個答案;先問「怎麼做到只讀該讀的位元組」,Parquet 的 encoding 選擇和 Iceberg 的四層 metadata 就變成同一個問題的兩種解法。每一層都在回答上一層留下的問題。

這層在問什麼
硬體與記憶體 為什麼欄式加向量化會比較快,什麼時候不會
儲存格式 怎麼做到只讀該讀的位元組
查詢引擎 DataFusion 怎麼把計劃變成執行
表格式 Iceberg 與 iceberg-rust 如何接上引擎
跨界 Comet 怎麼讓 JVM 和 Rust 共存

明天先從最底層開始:NULL 用 bitmask 的 0 還是 1 表示,對單一系統毫無差別,Arrow 為什麼要為這種事立規格?

那就明天見~



下一篇
Day 2 向量化執行跟 SIMD,是同一件事嗎?
系列文
1+1+1>3 ~ Spark 與 DataFusion、Comet 效能煉金術 ~2
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言